package com.szchenya.epermit.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 日志统一打印切面
 *
 * @author lovelyhedong
 * @date 2021年12月31日 14:05:36
 */
@Component
@Aspect
public class LogAspect {

    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

    /**
     * 定义切点Pointcut,如果需要其他controller,按需添加: ||execution (* com.xxx.xxxPackegName..*.*(..))
     */
    @Pointcut("execution (* com.szchenya.epermit.controller.*.*(..))")
    public void executeController() {
    }

    @Around("executeController()")
    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //方法路径
        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        //IP地址
        String iP = getIp(request);
        //请求入参
        String requestParam = JSON.toJSONString(Arrays.stream(joinPoint.getArgs())
                .filter(param -> !(param instanceof HttpServletRequest)
                        && !(param instanceof HttpServletResponse)
                        && !(param instanceof MultipartFile)
                        && !(param instanceof MultipartFile[])
                        && !(param instanceof Model)
                ).collect(Collectors.toList()));

        logger.info("\nController名字为->{}, 类和方法名为->{}, IP->{}, 请求入参为->{}", getControllerApiOperationValue(joinPoint), methodName, iP, requestParam);

        long begin = System.currentTimeMillis();
        Object result = joinPoint.proceed();

        logger.info("\nController名字为->{}, 耗时->{}ms, 返回参数为->{}", getControllerApiOperationValue(joinPoint), System.currentTimeMillis() - begin, JSONObject.toJSONString(result));
        return result;
    }

    /**
     * 获取目标主机的ip
     *
     * @param request
     * @return
     */
    private String getIp(HttpServletRequest request) {
        List<String> ipHeadList = Stream.of("X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "X-Real-IP").collect(Collectors.toList());
        for (String ipHead : ipHeadList) {
            if (checkIP(request.getHeader(ipHead))) {
                return request.getHeader(ipHead).split(",")[0];
            }
        }
        return "0:0:0:0:0:0:0:1".equals(request.getRemoteAddr()) ? "127.0.0.1" : request.getRemoteAddr();
    }

    /**
     * 检查ip存在
     */
    private boolean checkIP(String ip) {
        return !(null == ip || 0 == ip.length() || "unknown".equalsIgnoreCase(ip));
    }

    /**
     * 获取Controller的方法名
     */
    private String getControllerApiOperationValue(ProceedingJoinPoint joinPoint) {
        Method[] methods = joinPoint.getSignature().getDeclaringType().getMethods();
        for (Method method : methods) {
            if (StringUtils.endsWithIgnoreCase(method.getName(), joinPoint.getSignature().getName())) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                if (!Objects.isNull(apiOperation) && !Objects.isNull(apiOperation.value())) {
                    return apiOperation.value();
                }
            }
        }
        return "该Controller的方法使用未使用注解@ApiOperation";
    }

}
